package com.example.day31_video_clip;

import android.annotation.SuppressLint;
import android.content.Context;
import android.media.AudioFormat;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMetadataRetriever;
import android.media.MediaMuxer;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * Description:
 * Created by ZhDu on 2021/1/4 14:01.
 */
@SuppressWarnings("deprecation")
public class MusicProcess {
    private static final String TAG = "MusicProcess";
    public static final int TIMEOUT = 1000;

    /**
     * 混合2个PCM
     *
     * @param pcm1Path 第一个pcm路径
     * @param pcm2Path 第二个pcm路径
     * @param toPath   生成的文件的路径
     * @param volume1  第一个pcm的音量
     * @param volume2  第二个pcm的音量
     */
    public static void mixPcm(String pcm1Path, String pcm2Path, String toPath, int volume1, int volume2) throws IOException {
        float vol1 = normalizeVolume(volume1);
        float vol2 = normalizeVolume(volume2);
        byte[] buffer1_pcm1 = new byte[2048];
        byte[] buffer2_pcm2 = new byte[2048];
        byte[] buffer3_out = new byte[2048];

        FileInputStream is1 = new FileInputStream(pcm1Path);
        FileInputStream is2 = new FileInputStream(pcm2Path);
        FileOutputStream fileOutputStream = new FileOutputStream(toPath);

        short temp1, temp2;
        int temp;
        boolean end1 = false, end2 = false;
        try {
            while (!end1 || !end2) {//2个都没结束
                if (!end1) {
                    Log.i("MusicProcess", "is1.read===" + is1.read(buffer1_pcm1) );
                    Log.i("MusicProcess", "is1===" + is1.read() );
                    end1 = (is1.read(buffer1_pcm1) == -1);//判断是否把buffer1读取完
                    //把音乐文件的数据读取到buffer3
                    System.arraycopy(buffer1_pcm1, 0, buffer3_out, 0, buffer1_pcm1.length);
                }
                if (!end2) {
                    end2 = (is2.read(buffer2_pcm2) == -1);//判断是否把buffer2读取完
                    int voice = 0;//声音的值  跳过下一个声音的值    一个声音是2 个字节
                    for (int i = 0; i < buffer2_pcm2.length; i += 2) {
                        //将buffer里音量的数值取出来
                        temp1 = (short) ((buffer1_pcm1[i] & 0xff) | (buffer1_pcm1[i + 1] & 0xff) << 8);
                        temp2 = (short) ((buffer2_pcm2[i] & 0xff) | (buffer2_pcm2[i + 1] & 0xff) << 8);
                        //进行音频合成
                        temp = (int) (temp1 * vol1 + temp2 * vol2);//音乐和 视频声音 各占一半
                        if (temp > 32767) {
                            temp = 32767;
                        } else if (temp < -32768) {
                            temp = -32768;
                        }
                        //将数值转化成pcm
                        buffer3_out[i] = (byte) (temp & 0xff);
                        buffer3_out[i + 1] = (byte) ((temp >>> 8) & 0xFF);
                        fileOutputStream.write(buffer3_out);
                    }
                }
            }
        }finally {
            is1.close();
            is2.close();
            fileOutputStream.close();
        }
    }

    private static float normalizeVolume(int volume) {
        //把要设置的音量转化为float，保留精度
        return volume / 100f * 1;
    }

    @SuppressLint("WrongConstant")
    /**
     * @param startTimeUs 开始时间
     * @param endTimeUs 结束时间
     * @param videoVolume 视频音量
     * @param accVolume 音频音量
     */
    public static void mixAudioTrack(Context context, String videoPath, String audioPath, String outPath, Integer startTimeUs,
                                     Integer endTimeUs, int videoVolume, int accVolume) throws Exception {
        final File videoPcmFile = new File(Environment.getExternalStorageDirectory(), "out1_video.pcm");
        final File musicPcmFile = new File(Environment.getExternalStorageDirectory(), "out2_music.pcm");
        MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
        mediaMetadataRetriever.setDataSource(audioPath);
        //读取音乐时间
        final int aacDurationMs = Integer.parseInt(mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
        mediaMetadataRetriever.release();
        //提取媒体里的格式内容
        MediaExtractor audioExtractor = new MediaExtractor();
        audioExtractor.setDataSource(audioPath);

        decodeToPCM(videoPath, videoPcmFile.getAbsolutePath(), startTimeUs, endTimeUs);
        final int videoDurationMs = (endTimeUs - startTimeUs) / 1000;
        /******/
        decodeToPCM(audioPath, musicPcmFile.getAbsolutePath(), startTimeUs, endTimeUs);
        final File mixPcmFile = new File(Environment.getExternalStorageDirectory(), "out3_mix.pcm");
        mixPcm(videoPcmFile.getAbsolutePath(), musicPcmFile.getAbsolutePath(), mixPcmFile.getAbsolutePath(), videoVolume, accVolume);
        File wavFile = new File(Environment.getExternalStorageDirectory(), "out3_mix.wav");
        new PcmToWavUtil(44100, AudioFormat.CHANNEL_IN_STEREO, 2, AudioFormat.ENCODING_PCM_16BIT).pcmToWav(
                mixPcmFile.getAbsolutePath(), wavFile.getAbsolutePath()
        );
        Log.i("MusicProcess", "mixAudioTrack===wav文件生成===");
        mixVideoAndMusic(videoPath, outPath, startTimeUs, endTimeUs, wavFile);
    }

    @SuppressLint("WrongConstant")
    public static void decodeToPCM(String musicPath, String outPath, int startTime, int endTime) throws Exception {
        if (endTime < startTime) {
            return;
        }
        //音频解封装  将MP3解成aac
        MediaExtractor mediaExtractor = new MediaExtractor();
        try {
            mediaExtractor.setDataSource(musicPath);
            int audioTrackIndex = selectTrack(mediaExtractor, true);
            mediaExtractor.selectTrack(audioTrackIndex);//将封装格式指向音频通道
            //跳到开始剪辑的位置
            mediaExtractor.seekTo(startTime, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
            MediaFormat oriAudioFormat = mediaExtractor.getTrackFormat(audioTrackIndex);
            int maxBufferSize = 100 * 1000;//一次读取的最大字节数，100k
            if (oriAudioFormat.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
                maxBufferSize = oriAudioFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
            } else {
                maxBufferSize = 100 * 1000;
            }
            ByteBuffer buffer = ByteBuffer.allocateDirect(maxBufferSize);
            MediaCodec mediacodec = MediaCodec.createDecoderByType(oriAudioFormat.getString(MediaFormat.KEY_MIME));
            //设置解码信息 直接从音频文件获取
            mediacodec.configure(oriAudioFormat, null, null, 0);
            File pcmFile = new File(outPath);
            FileChannel writeChannel = new FileOutputStream(pcmFile).getChannel();
            mediacodec.start();
            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            int outputBufferIndex = -1;
            while (true) {
                int decodeInputIndex = mediacodec.dequeueInputBuffer(1000);
                if (decodeInputIndex >= 0) {
                    //获取采样时间
                    long sampleTime = mediaExtractor.getSampleTime();
                    if (sampleTime == -1) {
                        break;
                    } else if (sampleTime < startTime) {
                        mediaExtractor.advance();//释放掉数据
                        continue;
                    } else if (sampleTime > endTime) {
                        break;
                    }
                    //获取到压缩数据
                    info.size = mediaExtractor.readSampleData(buffer, 0);
                    info.presentationTimeUs = sampleTime;
                    info.flags = mediaExtractor.getSampleFlags();

                    byte[] content = new byte[buffer.remaining()];
                    buffer.get(content);
//                    FileUtils.writeContent(content);//输出到文件
                    //解码
                    ByteBuffer inputBuffer = mediacodec.getInputBuffer(decodeInputIndex);
                    inputBuffer.put(content);
                    mediacodec.queueInputBuffer(decodeInputIndex, 0, info.size, info.presentationTimeUs, info.flags);
                    //释放上一帧的压缩数据
                    mediaExtractor.advance();
                }
                outputBufferIndex = mediacodec.dequeueOutputBuffer(info, 1_000);
                while (outputBufferIndex >= 0) {
                    ByteBuffer decodeOutputBuffer = mediacodec.getOutputBuffer(outputBufferIndex);
                    writeChannel.write(decodeOutputBuffer);//输出到out.pcm
                    mediacodec.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = mediacodec.dequeueOutputBuffer(info, 1_000);
                }
            }
            writeChannel.close();
            mediaExtractor.release();
            mediacodec.stop();
            mediacodec.release();
            {
                FileInputStream is = new FileInputStream(pcmFile);
                while (true){
                    Log.i("MusicProcess", "decodeToPCM====" + is.read());
                    if ((is.read()) == -1) {
                        break;
                    }
                }
            }
            //转换MP3    pcm数据转换成mp3封装格式
//            File wavFile = new File(Environment.getExternalStorageDirectory(), "output.mp3");
//            new PcmToWavUtil(44100, AudioFormat.CHANNEL_IN_STEREO, 2,
//                    AudioFormat.ENCODING_PCM_16BIT).pcmToWav(pcmFile.getAbsolutePath(), wavFile.getAbsolutePath());
            Log.i("MusicProcess", "转换完毕...");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void mixVideoAndMusic(String videoInput, String output, Integer startTimeUs, Integer endTimeUs, File wavFile) throws IOException {
        //初始化一个视频封装容器
        MediaMuxer mediaMuxer = new MediaMuxer(output, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        //轨道 可以装音频和视频
        MediaExtractor mediaExtractor = new MediaExtractor();
        mediaExtractor.setDataSource(videoInput);
        //拿到视频轨道的索引
        int videoIndex = selectTrack(mediaExtractor, false);

        int audioIndex = selectTrack(mediaExtractor, true);
        //视频配置文件
        MediaFormat videoFormat = mediaExtractor.getTrackFormat(videoIndex);
        //开辟一个轨道，写数据
        mediaMuxer.addTrack(videoFormat);
        //视频中的音频轨道
        MediaFormat audioFormat = mediaExtractor.getTrackFormat(audioIndex);
        //音轨的比特率
        int audioBitrate = audioFormat.getInteger(MediaFormat.KEY_BIT_RATE);
        audioFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC);
        //将音频写入轨道
        int muxerAudioIndex = mediaMuxer.addTrack(audioFormat);
        //开始输出
        mediaMuxer.start();
        //音频的wav中提取pcm
        MediaExtractor pcmExtrator = new MediaExtractor();
        pcmExtrator.setDataSource(wavFile.getAbsolutePath());
        int audioTrack = selectTrack(pcmExtrator, true);
        //设置音频的轨道
        pcmExtrator.selectTrack(audioTrack);
        MediaFormat pcmTrackFormat = pcmExtrator.getTrackFormat(audioTrack);
        //最大一帧的大小
        int maxBufferSize = 0;
        if (audioFormat.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
            maxBufferSize = pcmTrackFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
        } else {
            maxBufferSize = 100 * 1000;
        }
        //输出
        MediaFormat encodeFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC,
                44100,2);
        //设置比特率
        encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE,audioBitrate);
        //设置音质等级
        encodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
        //设置最大一帧的大小
        encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE,maxBufferSize);
        //编码
        MediaCodec encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
        encoder.configure(encodeFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
        encoder.start();
        ByteBuffer buffer = ByteBuffer.allocateDirect(maxBufferSize);
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        boolean encodeDone = false;
        while (!encodeDone){
            int inputBufferIndex = encoder.dequeueInputBuffer(10000);
            if (inputBufferIndex >= 0) {
                long sampleTime = pcmExtrator.getSampleTime();
                if (sampleTime < 0) {//pts小于0，来到文件末尾，不用编码
                    encoder.queueInputBuffer(inputBufferIndex, 0,
                            0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                }else {
                    int flags = pcmExtrator.getSampleFlags();
                    int size = pcmExtrator.readSampleData(buffer,0);
                    ByteBuffer inputBuffer = encoder.getInputBuffer(inputBufferIndex);
                    inputBuffer.clear();
                    inputBuffer.put(buffer);
                    inputBuffer.position(0);
                    encoder.queueInputBuffer(inputBufferIndex,0,size,sampleTime,flags);
                    pcmExtrator.advance();
                }
            }
            //获取编码完的数据
            int outputBufferIndex = encoder.dequeueOutputBuffer(info,TIMEOUT);
            while (outputBufferIndex>=0){
                if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
                    encodeDone = true;
                    break;
                }
                ByteBuffer encodeOutputBuffer = encoder.getOutputBuffer(outputBufferIndex);
                //将数据写入音轨
                mediaMuxer.writeSampleData(muxerAudioIndex,encodeOutputBuffer,info);
                encodeOutputBuffer.clear();
                encoder.releaseOutputBuffer(outputBufferIndex,false);
                outputBufferIndex = encoder.dequeueOutputBuffer(info, TIMEOUT);
            }
        }
        if (audioTrack >= 0) {
            mediaExtractor.unselectTrack(audioTrack);//释放所选择的轨道
        }
        mediaExtractor.selectTrack(videoIndex);
        mediaExtractor.seekTo(startTimeUs,MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
        maxBufferSize = videoFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
        buffer = ByteBuffer.allocateDirect(maxBufferSize);
        while(true){
            long sampleTimeUs = mediaExtractor.getSampleTime();
            if (sampleTimeUs == -1) {
                break;
            }
            if (sampleTimeUs < startTimeUs) {//抛掉设置的开始时间前的数据
                mediaExtractor.advance();
                continue;
            }
            if (endTimeUs != null && sampleTimeUs > endTimeUs) {
                break;
            }
            info.presentationTimeUs = sampleTimeUs-startTimeUs+600;//开始的时间
            info.flags = mediaExtractor.getSampleFlags();
            info.size = mediaExtractor.readSampleData(buffer,0);
            if (info.size < 0) {
                break;
            }
            mediaMuxer.writeSampleData(videoIndex,buffer,info);
            mediaExtractor.advance();
        }
        try {
            pcmExtrator.release();
            mediaExtractor.release();
            encoder.stop();
            encoder.release();
            mediaMuxer.release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //一个音频文件里有很多个通道，找到音频通道
    private static int selectTrack(MediaExtractor mediaExtractor, boolean isAudio) {
        //获取所有通道数
        int trackCount = mediaExtractor.getTrackCount();
        for (int i = 0; i < trackCount; i++) {
            MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
            //获取通道当前的格式
            String mime = trackFormat.getString(MediaFormat.KEY_MIME);
            if (isAudio) {
                if (mime.startsWith("audio/")) {
                    return i;
                }
            } else {
                if (mime.startsWith("video/")) {
                    return i;
                }
            }
        }
        return -1;
    }

}
